Skip to content

MorphTx enhances and updates Jade upgrades#889

Open
SegueII wants to merge 5 commits intomainfrom
ref_key
Open

MorphTx enhances and updates Jade upgrades#889
SegueII wants to merge 5 commits intomainfrom
ref_key

Conversation

@SegueII
Copy link
Contributor

@SegueII SegueII commented Feb 27, 2026

Summary by CodeRabbit

  • New Features

    • V2 block handling APIs for sequencer workflows (block retrieval, apply, and latest queries).
    • Jade fork support for upgraded fork-time handling.
  • Refactor

    • Renamed fork-related state and logs to Jade terminology.
    • Updated internal transaction decoding to use Morph transaction type mapping.
  • Chores

    • Bumped go-ethereum dependency across modules.

@SegueII SegueII requested a review from a team as a code owner February 27, 2026 04:33
@SegueII SegueII requested review from panos-xyz and removed request for a team February 27, 2026 04:33
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 27, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Renames fork handling from MPT→Jade, adds Executor L2NodeV2 methods and RetryableClient V2 RPCs, updates blob tx decoding to use MorphTx, and bumps go-ethereum dependency/submodule and Makefile ETHEREUM_* variables to the newer commit/version.

Changes

Cohort / File(s) Summary
Go-ethereum / Makefile
Makefile, go-ethereum
Updated Makefile variables: ETHEREUM_SUBMODULE_COMMIT_OR_TAGmain, ETHEREUM_TARGET_VERSIONv1.10.14-0.20260306065034-81fb39343fd1. Updated go-ethereum submodule pointer to commit 81fb39343fd1.
Module dependency bumps
bindings/go.mod, contracts/go.mod, node/go.mod, ops/l2-genesis/go.mod, ops/tools/go.mod, oracle/go.mod, token-price-oracle/go.mod, tx-submitter/go.mod
Bumped github.com/morph-l2/go-ethereum require lines to v1.10.14-0.20260306065034-81fb39343fd1 across multiple modules.
Fork terminology migration (MPT → Jade)
node/core/batch.go, node/core/executor.go, node/types/retryable_client.go
Renamed fork-related fields/methods/logs and JSON tags: mptForkTimejadeForkTime, mptForkStagejadeForkStage, mptForkForceHeightjadeForkForceHeight, forceBatchPointForMPTFork()forceBatchPointForJadeFork(), MPTForkTime()JadeForkTime(). Updated control flow and logging to use Jade identifiers.
Executor L2NodeV2 & RetryableClient V2 RPCs
node/core/executor.go, node/types/retryable_client.go
Added Executor public methods for V2 blocks: RequestBlockDataV2, ApplyBlockV2, GetBlockByNumber, GetLatestBlockV2. Added AssembleL2BlockV2 and BlockByNumber RPC wrappers with retry semantics to RetryableClient. Included private conversion helpers between BlockV2 and ExecutableL2Data.
Blob tx decoding change
node/types/blob.go
In DecodeTxsFromBytes, switched handling to treat the Morph tx second-level type as MorphTxType and instantiate MorphTx (replacing prior AltFeeTx instantiation).
Misc small edits
various files modified for logging/comments
Updated log strings, comments, and identifiers to reflect Jade fork naming and V2 flow; no additional public API changes beyond above.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Sequencer as Sequencer/Client
    participant Executor as Executor
    participant RC as RetryableClient
    participant Storage as BlockStorage
    rect rgba(200,230,255,0.5)
    Sequencer->>Executor: RequestBlockDataV2(parentHashBytes)
    Executor->>RC: AssembleL2BlockV2(ctx, parentHash, transactions)
    RC-->>Executor: ExecutableL2Data (or error)
    Executor->>Executor: convert ExecutableL2Data -> BlockV2
    Executor->>Storage: ApplyBlockV2(block)
    Storage-->>Executor: ack / persist result
    Executor-->>Sequencer: BlockV2 / success
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • panos-xyz
  • curryxbo
  • FletcherMan
  • chengwenxi

Poem

🐰 I hopped through code with jaunty stride,
Jade replaced MPT, a glossy tide,
V2 blocks whisper, transactions morph,
Dependencies updated — a neat little north,
Hoppity deploys! 🌿✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is vague and uses imprecise language; 'enhances and updates' lacks specificity about what is being changed or improved. Clarify the title to be more specific about the main changes, such as 'Update go-ethereum dependency and rename MPT fork to Jade fork' or 'Migrate MPT fork to Jade fork with L2NodeV2 interface support'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ref_key

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
node/core/executor.go (1)

347-353: Inconsistent terminology: error message still references "MPT".

The error message at line 351 references "MPT-compatible geth" while the rest of this PR refactors to "Jade fork" terminology. Similar issue at lines 390-391.

Consider updating for consistency, or clarify if "MPT" is intentionally retained for user-facing messages.

♻️ Suggested terminology update
 		e.logger.Error("!!! CRITICAL: Geth is behind - node BLOCKED !!!",
 			"consensus_block", wrappedBlock.Number,
 			"geth_height", height,
-			"action", "Switch to MPT-compatible geth IMMEDIATELY")
+			"action", "Switch to Jade-compatible geth IMMEDIATELY")

And at lines 390-391:

-		e.logger.Error("HINT: If this occurs after MPT upgrade, your geth node may not support MPT blocks. " +
-			"Please ensure you are running an MPT-compatible geth node.")
+		e.logger.Error("HINT: If this occurs after Jade upgrade, your geth node may not support Jade blocks. " +
+			"Please ensure you are running a Jade-compatible geth node.")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@node/core/executor.go` around lines 347 - 353, Update the user-facing error
strings to use the new "Jade fork" terminology instead of "MPT" for consistency:
modify the logger.Error calls that reference "MPT-compatible geth" (the call
that logs "!!! CRITICAL: Geth is behind - node BLOCKED !!!" using
wrappedBlock.Number, height and returns types.ErrWrongBlockNumber) and the
similar messages around the block checks at the later error paths (the
logger.Error usage around lines ~390-391) so they mention "Jade fork" (or the
agreed new phrase) consistently; keep the same context parameters
(consensus_block, geth_height, action) and return types.ErrWrongBlockNumber as
before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Makefile`:
- Around line 2-3: The Makefile currently sets ETHEREUM_SUBMODULE_COMMIT_OR_TAG
to a non-existent tag `test_3_13`, which causes git checkout failures; update
the variable ETHEREUM_SUBMODULE_COMMIT_OR_TAG to a valid go-ethereum tag or
commit hash (and optionally align ETHEREUM_TARGET_VERSION if it must match) by
replacing `test_3_13` with an actual tag/commit present in the go-ethereum repo
and verify by running the same git checkout command used in the Makefile to
ensure it succeeds.

In `@node/types/blob.go`:
- Around line 156-160: The code references non-existent symbols MorphTxType and
MorphTx (seen where you read firstByte and set innerTx = new(eth.MorphTx)),
which will fail to compile; replace these with the correct go-ethereum custom tx
symbols AltFeeTxType and AltFeeTx (or update the go-ethereum dependency if you
intend to use Morph types), and ensure the switch case uses eth.AltFeeTxType and
innerTx = new(eth.AltFeeTx) so binary.Read and subsequent handling use the
proper types.

---

Nitpick comments:
In `@node/core/executor.go`:
- Around line 347-353: Update the user-facing error strings to use the new "Jade
fork" terminology instead of "MPT" for consistency: modify the logger.Error
calls that reference "MPT-compatible geth" (the call that logs "!!! CRITICAL:
Geth is behind - node BLOCKED !!!" using wrappedBlock.Number, height and returns
types.ErrWrongBlockNumber) and the similar messages around the block checks at
the later error paths (the logger.Error usage around lines ~390-391) so they
mention "Jade fork" (or the agreed new phrase) consistently; keep the same
context parameters (consensus_block, geth_height, action) and return
types.ErrWrongBlockNumber as before.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c00f42 and bb760d0.

⛔ Files ignored due to path filters (9)
  • bindings/go.sum is excluded by !**/*.sum
  • contracts/go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
  • node/go.sum is excluded by !**/*.sum
  • ops/l2-genesis/go.sum is excluded by !**/*.sum
  • ops/tools/go.sum is excluded by !**/*.sum
  • oracle/go.sum is excluded by !**/*.sum
  • token-price-oracle/go.sum is excluded by !**/*.sum
  • tx-submitter/go.sum is excluded by !**/*.sum
📒 Files selected for processing (14)
  • Makefile
  • bindings/go.mod
  • contracts/go.mod
  • go-ethereum
  • node/core/batch.go
  • node/core/executor.go
  • node/go.mod
  • node/types/blob.go
  • node/types/retryable_client.go
  • ops/l2-genesis/go.mod
  • ops/tools/go.mod
  • oracle/go.mod
  • token-price-oracle/go.mod
  • tx-submitter/go.mod

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
node/types/retryable_client.go (1)

445-461: ⚠️ Potential issue | 🟠 Major

Bug: BlockByNumber bypasses client switching logic.

BlockByNumber uses rc.ethClient directly instead of rc.eClient(). This bypasses the client switching mechanism, which could cause the method to query the wrong client after a switch has occurred.

🐛 Proposed fix
 func (rc *RetryableClient) BlockByNumber(ctx context.Context, blockNumber *big.Int) (ret *eth.Block, err error) {
 	if retryErr := backoff.Retry(func() error {
-		resp, respErr := rc.ethClient.BlockByNumber(ctx, blockNumber)
+		resp, respErr := rc.eClient().BlockByNumber(ctx, blockNumber)
 		if respErr != nil {
 			rc.logger.Info("failed to call BlockByNumber", "error", respErr)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@node/types/retryable_client.go` around lines 445 - 461, BlockByNumber
currently calls rc.ethClient directly, bypassing the client-switching logic in
rc.eClient(); change the call to use rc.eClient().BlockByNumber so the method
always uses the active client returned by eClient(), preserve the existing
retry/backoff flow and error handling (including retryableError checks and
setting err/ret), and ensure ret is only assigned when resp is non-nil; update
references inside the anonymous retry func to use eClient() instead of
ethClient.
🧹 Nitpick comments (16)
node/sync/bridge_client.go (1)

27-30: Optional: Consider returning a read-only interface instead of concrete *ethclient.Client.

While the current callers only read from the returned client and no .Close() calls are present in the codebase, returning a concrete type exposes unnecessary mutability. The existing comment documents the intent ("for sharing with other services"), but narrowing the API surface to a read-only interface would make ownership boundaries clearer and prevent accidental misuse by future callers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@node/sync/bridge_client.go` around lines 27 - 30, Change
BridgeClient.L1Client to return a narrow read-only interface instead of
*ethclient.Client: declare a new interface (e.g., ReadOnlyEthClient or
L1ClientReader) in the same package that lists only the methods callers actually
use, update the method signature func (c *BridgeClient) L1Client()
ReadOnlyEthClient, and return c.l1Client (ethclient.Client implements the
interface). This preserves behavior but prevents callers from calling mutating
methods like Close(); ensure the interface includes all methods currently
invoked on the returned client so the code continues to compile.
ops/docker-sequencer-test/run-test.sh (4)

471-471: Fix inconsistent indentation.

Line 471 has an extra leading space before echo.

♻️ Proposed fix
-     echo "Node 0 (seq-0): Block $(get_block_number http://127.0.0.1:8545)"
+    echo "Node 0 (seq-0): Block $(get_block_number http://127.0.0.1:8545)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/docker-sequencer-test/run-test.sh` at line 471, The echo line has an
extra leading space causing inconsistent indentation; remove the extra leading
space so the line reads starting at column 1 (i.e., align `echo "Node 0 (seq-0):
Block $(get_block_number http://127.0.0.1:8545)"` with surrounding lines),
keeping the same content and using the existing `get_block_number` call.

274-274: Document the well-known test private key.

This is the Anvil/Hardhat account #0 private key (a widely-used development key). Add a comment to clarify this is intentional and test-only.

📝 Proposed documentation
+    # Anvil/Hardhat account `#0` - well-known test key, DO NOT use in production
     export SEQUENCER_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/docker-sequencer-test/run-test.sh` at line 274, The exported
SEQUENCER_PRIVATE_KEY value in run-test.sh is the well-known Anvil/Hardhat
account `#0` private key used for local/dev testing; add an inline comment next to
the export of SEQUENCER_PRIVATE_KEY clarifying that this is the intentional,
widely-used development/test-only key (not for production) so readers understand
it's safe and expected in tests.

68-68: Shellcheck SC2155: Declare and assign separately.

Multiple lines combine local declaration with command substitution, which masks return values. While this is low-risk for a test script, separating declaration and assignment improves robustness.

♻️ Example fix pattern
-    local current=$(get_block_number "$rpc_url")
+    local current
+    current=$(get_block_number "$rpc_url")

Also applies to: 305-305, 313-314, 333-333, 342-342, 345-346, 360-360, 378-378

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/docker-sequencer-test/run-test.sh` at line 68, Several local variables
are declared with command substitution in one step (e.g., local
current=$(get_block_number "$rpc_url")), which triggers ShellCheck SC2155;
change each to declare the variable first and assign separately to avoid masking
return values. For each occurrence (functions like get_block_number and
variables such as current, target, next, etc.), replace the single-line "local
var=$(...)" with two lines: "local var" followed by "var=$(...)" ensuring you
preserve any quoting and command arguments (e.g., "$rpc_url"); apply this
pattern to all listed occurrences.

142-240: Embedded Python script is complex but functional.

The inline Python for contract deployment and genesis generation is extensive. Consider extracting this to a separate Python script file for better maintainability and testability. However, keeping it inline does ensure the script is self-contained.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/docker-sequencer-test/run-test.sh` around lines 142 - 240, Extract the
large inline Python payload into a separate script (e.g., generate_genesis.py)
and replace the embedded python3 -c "..." block in run-test.sh with a call to
that script; move all top-level symbols and logic (sys.path.insert(0,
'$DEVNET_DIR'), imports devnet, functions/variables like run_command, read_json,
write_json, log, devnet_cfg_orig, temp_deploy_config, genesis_batch_header,
addresses, etc.) into the new file, accept required environment values
(DEVNET_DIR, MORPH_ROOT, OPS_DIR, CONTRACTS_DIR or pass them as args/env), add a
proper shebang and make it executable, and ensure the shell script invokes
python3 generate_genesis.py with the same environment so return codes and
side-effects (files written to devnet_dir, deployment_dir, .env updates) are
preserved.
ops/docker-sequencer-test/docker-compose.override.yml (2)

3-3: version field is obsolete in Docker Compose v2+.

The version key is ignored by modern Docker Compose and produces a warning. Consider removing it.

♻️ Proposed fix
 # Override file to use test images
 # Copy this to ops/docker/docker-compose.override.yml before running test
-version: '3.8'
 
 services:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/docker-sequencer-test/docker-compose.override.yml` at line 3, Remove the
obsolete top-level "version:" key from docker-compose.override.yml (the
"version: '3.8'" line) because Compose v2+ ignores it and emits a warning;
simply delete that key and its value so the file uses the newer schema without
the version field.

27-27: Document that these are test-only private keys.

Gitleaks flagged these as potential API key leaks. While these appear to be pre-generated test keys (similar to well-known Anvil/Hardhat development keys), adding a comment will prevent future confusion and silence security scanners.

📝 Proposed documentation
   node-0:
     image: morph-node-test:latest
     build:
       context: ../..
       dockerfile: ops/docker-sequencer-test/Dockerfile.l2-node-test
     environment:
+      # Test-only sequencer key - DO NOT use in production
       - MORPH_NODE_SEQUENCER_PRIVATE_KEY=0xd99870855d97327d20c666abc78588f1449b1fac76ed0c86c1afb9ce2db85f32
   node-1:
     image: morph-node-test:latest
     environment:
+      # Test-only sequencer key - DO NOT use in production
       - MORPH_NODE_SEQUENCER_PRIVATE_KEY=0x0890c388c3bf5e04fee1d8f3c117e5f44f435ced7baf7bfd66c10e1f3a3f4b10

Also applies to: 36-36

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/docker-sequencer-test/docker-compose.override.yml` at line 27, Add an
inline comment next to the MORPH_NODE_SEQUENCER_PRIVATE_KEY environment variable
to clearly state that this value is a pre-generated test/development private key
(not for production) so scanners and future readers know it's safe to commit;
update the same comment for the other identical key occurrence (the second
MORPH_NODE_SEQUENCER_PRIVATE_KEY entry) to match.
ops/docker-sequencer-test/Dockerfile.l2-geth-test (1)

12-17: Consider using a minimal runtime image.

The runtime stage uses the full go-ubuntu-builder image which includes Go toolchain and build dependencies. For a smaller image, consider using a minimal base like ubuntu:22.04 or debian:bookworm-slim since only the compiled binary is needed at runtime.

💡 Example using minimal base
 # Runtime stage
-FROM ghcr.io/morph-l2/go-ubuntu-builder:go-1.24-ubuntu
+FROM ubuntu:22.04

 RUN apt-get -qq update && apt-get -qq install -y --no-install-recommends \
     ca-certificates bash curl \
     && rm -rf /var/lib/apt/lists/*
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/docker-sequencer-test/Dockerfile.l2-geth-test` around lines 12 - 17, The
runtime stage currently uses the full builder image
(ghcr.io/morph-l2/go-ubuntu-builder:go-1.24-ubuntu); replace it with a minimal
runtime base such as ubuntu:22.04 or debian:bookworm-slim, install only the
runtime packages (ca-certificates, bash, curl) as already present, and adjust
any subsequent COPY/USER instructions that expect the built binary (ensure the
compiled binary built in the builder stage is copied into the new minimal stage
and ownership/permissions are set appropriately). Keep the existing RUN apt-get
lines but run them against the new minimal base and remove any leftover
build-tool assumptions from the runtime stage.
ops/docker-sequencer-test/Dockerfile.l2-node-test (1)

37-47: Consider using a minimal runtime image (consistent with geth Dockerfile).

Same as the geth test Dockerfile, the runtime stage uses the full builder image. For consistency and smaller images, consider using a minimal base since only the compiled binaries are needed at runtime.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/docker-sequencer-test/Dockerfile.l2-node-test` around lines 37 - 47, The
final stage currently uses the full builder image (FROM
ghcr.io/morph-l2/go-ubuntu-builder:go-1.24-ubuntu) even though only the compiled
binaries are needed; change the final stage to a minimal runtime base (e.g., a
distroless or slim image) and re-add any required runtime packages
(ca-certificates or libc) if needed, then keep the COPY lines (COPY
--from=builder /bitget/morph/node/build/bin/tendermint /usr/local/bin/ and COPY
--from=builder /bitget/morph/node/build/bin/morphnode /usr/local/bin/) and the
CMD (["morphnode","--home","/data"]) so only the two binaries are included in
the smaller image.
ops/docker-sequencer-test/scripts/tx-generator.sh (1)

31-36: Code duplication with run-test.sh.

This get_block_number function is nearly identical to the one in run-test.sh (lines 53-59). Consider extracting shared utilities into a common script that both can source to avoid maintenance drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ops/docker-sequencer-test/scripts/tx-generator.sh` around lines 31 - 36, The
get_block_number function is duplicated between tx-generator.sh and run-test.sh;
extract this function into a shared utility script (e.g., a sourced common util
script) and replace the duplicate definitions by sourcing that shared script
from both tx-generator.sh and run-test.sh, ensuring the function name
get_block_number remains unchanged so callers keep working and update any
shebangs/execute permissions as needed.
node/l1sequencer/tracker.go (1)

54-68: Consider an immediate check on startup.

The loop only triggers on ticker ticks, so the first sync lag check happens 1 minute after start. Consider adding an immediate check before entering the loop to detect issues earlier.

🔧 Suggested improvement
 func (t *L1Tracker) loop() {
 	defer close(t.stop)
 
+	// Immediate check on startup
+	t.checkL1SyncLag()
+
 	ticker := time.NewTicker(1 * time.Minute)
 	defer ticker.Stop()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@node/l1sequencer/tracker.go` around lines 54 - 68, The loop in L1Tracker.loop
delays the first sync check by up to one minute; call t.checkL1SyncLag() once
immediately before entering the for/select loop so the first check runs on
startup (ensure you still respect t.ctx.Done() by returning if context is
canceled), then keep the existing ticker-driven checks; reference the
L1Tracker.loop function and the checkL1SyncLag method to locate where to add the
immediate invocation.
node/flags/flags.go (1)

3-6: Import order should follow Go conventions.

Standard library packages should be grouped before third-party packages. Consider reordering:

 import (
-	"github.com/urfave/cli"
 	"time"
+
+	"github.com/urfave/cli"
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@node/flags/flags.go` around lines 3 - 6, The import block in flags.go
violates Go convention by placing the third-party package github.com/urfave/cli
before the standard library package time; reorder the imports so standard
library packages (time) appear first, then a blank line, then third-party
packages (github.com/urfave/cli) to follow Go import grouping conventions.
contracts/contracts/l1/L1Sequencer.sol (1)

9-13: Consider adding a storage gap for future upgrades.

For upgradeable contracts, it's a best practice to reserve storage slots for future state variables. Without a gap, adding new variables in future upgrades could corrupt existing storage layout.

🛡️ Suggested addition
 contract L1Sequencer is OwnableUpgradeable {
     // ============ Storage ============
 
     /// `@notice` Current sequencer address
     address public sequencer;
+
+    /// `@dev` Reserved storage gap for future upgrades
+    uint256[49] private __gap;
 
     // ============ Events ============
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/contracts/l1/L1Sequencer.sol` around lines 9 - 13, The contract
L1Sequencer (which inherits OwnableUpgradeable) lacks a reserved storage gap and
so adding new state vars in upgrades could corrupt the storage layout; add a
private reserved storage array (e.g., uint256[n] __gap) after the declared state
variable `address public sequencer;` (inside the storage section of L1Sequencer)
to reserve future slots and preserve compatibility with OwnableUpgradeable and
future upgrades.
node/core/executor.go (1)

662-683: Consider consistent nil Transactions handling in executableL2DataToBlockV2.

blockV2ToExecutableL2Data (lines 639-643) ensures Transactions is never nil, but executableL2DataToBlockV2 doesn't apply the same treatment. While this may be fine if the caller always provides non-nil data, it's inconsistent.

♻️ Suggested consistency improvement
 func executableL2DataToBlockV2(data *catalyst.ExecutableL2Data) *l2node.BlockV2 {
 	if data == nil {
 		return nil
 	}
+	txs := data.Transactions
+	if txs == nil {
+		txs = make([][]byte, 0)
+	}
 	return &l2node.BlockV2{
 		ParentHash:         data.ParentHash,
 		Miner:              data.Miner,
 		Number:             data.Number,
 		GasLimit:           data.GasLimit,
 		BaseFee:            data.BaseFee,
 		Timestamp:          data.Timestamp,
-		Transactions:       data.Transactions,
+		Transactions:       txs,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@node/core/executor.go` around lines 662 - 683, executableL2DataToBlockV2
currently copies Transactions directly and can leave BlockV2.Transactions nil,
making it inconsistent with blockV2ToExecutableL2Data which ensures Transactions
is never nil; update executableL2DataToBlockV2 to defensive-copy or set
Transactions to an empty slice when data.Transactions is nil (i.e., ensure
BlockV2.Transactions = data.Transactions or make([]<txnType>, 0) if nil) so both
conversion functions guarantee a non-nil Transactions slice.
node/l1sequencer/verifier.go (1)

59-83: IsSequencer logic has potential for double RPC call.

The current flow can make two L1 RPC calls in a single IsSequencer invocation:

  1. Line 67: Refresh if cache expired
  2. Line 78: Force refresh on cache miss

While this ensures correctness (catching recent sequencer changes), be aware of the RPC overhead. With a 10-second TTL, this is manageable, but with a longer TTL the double-call on miss becomes more relevant.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@node/l1sequencer/verifier.go` around lines 59 - 83, IsSequencer can perform
two RPC calls by calling flushCache twice (once for expired cache and again on
cache miss); modify IsSequencer to avoid a redundant second refresh by tracking
whether a refresh already occurred in this invocation—call flushCache once if
cache is expired, set a local flag (e.g., refreshed), check the cached sequencer
(c.sequencer) and only call flushCache again on miss if refreshed is false; keep
the existing mutex locking and return semantics and reference functions/fields
IsSequencer, flushCache, c.cacheExpiry, c.sequencer, and c.mutex.
node/l1sequencer/signer.go (1)

65-71: Consider whether nil verifier should be an error or expected state.

The IsActiveSequencer method returns an error when verifier is nil. However, NewLocalSigner accepts a nil verifier without error. This creates an asymmetry where construction succeeds but a key method fails.

If a nil verifier is a valid configuration (e.g., verifier disabled), consider returning (false, nil) instead. If a verifier is always required for proper operation, validate this in the constructor.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@node/l1sequencer/signer.go` around lines 65 - 71, The constructor/usage
mismatch: NewLocalSigner currently accepts a nil verifier but IsActiveSequencer
treats nil verifier as an error; pick one of two fixes and implement it
consistently: (A) If verifier is optional, change LocalSigner.IsActiveSequencer
to return (false, nil) when s.verifier == nil (remove the fmt.Errorf path) so
caller sees "not active" without error; or (B) If verifier is required, validate
in NewLocalSigner (e.g., return an error) and ensure NewLocalSigner does not
construct a LocalSigner with a nil verifier. Refer to the LocalSigner type, the
IsActiveSequencer method, and NewLocalSigner to locate where to apply the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bindings/bindings/l1sequencer.go`:
- Around line 32-36: The L1SequencerMetaData ABI is out of date: its initialize
signature only has initialize(address _owner) but the Solidity contract's
initialize requires initialize(address _owner, address _initialSequencer);
regenerate the Go bindings so L1SequencerMetaData.ABI includes the two-argument
initialize(address,address). Locate the binding struct L1SequencerMetaData and
re-run your binding generation tool (e.g., abigen / your build script) against
the updated L1Sequencer.sol contract so the generated ABI and Bin reflect the
new initialize(_owner,_initialSequencer) signature.

In `@contracts/deploy/022-SequencerInit.ts`:
- Around line 48-51: The call to IL1SequencerProxy.upgradeToAndCall (with
L1SequencerFactory.interface.encodeFunctionData('initialize', [owner,
initialSequencer])) returns a ContractTransaction that must be awaited for
mining; change the code to capture the returned tx, call await tx.wait() and
handle errors (e.g., try/catch or rethrow) before running awaitCondition so the
script can detect revert/failure early and avoid extra polling for an unmined
transaction.
- Around line 53-61: The call to awaitCondition(...) is passing rate and
attempts swapped (currently 3000, 1000) which yields an excessive wait; update
the call where IL1SequencerProxy.implementation() is compared to
L1SequencerImplAddress to use a reasonable retry strategy such as
awaitCondition(cond, 1000, 10) (rate = 1000ms, attempts = 10) or, if you
intended a very small number of retries, use attempts = 3; keep the condition
function as-is and only change the numeric arguments.

In `@node/l1sequencer/verifier.go`:
- Around line 16-20: The CacheTTL constant in verifier.go is set to 10 seconds
(CacheTTL) which is a test value; restore it to a production-safe default (30 *
time.Minute) or make it configurable during initialization and use that
configurable value where the cache is constructed so IsSequencer()'s
double-fetch won't cause excessive L1 RPCs; update the CacheTTL constant or add
a constructor parameter/field (e.g., sequencer verifier init/config) and ensure
IsSequencer() reads the configured TTL instead of the hard-coded 10s.

In `@node/types/retryable_client.go`:
- Around line 532-548: AssembleL2BlockV2 is calling rc.authClient directly which
bypasses the client-switching logic; change the call to use the accessor method
rc.aClient() (or the equivalent client-returning helper used elsewhere) when
invoking AssembleL2BlockV2 so the retry logic uses the current active client;
update the line that calls rc.authClient.AssembleL2BlockV2(...) to call
rc.aClient().AssembleL2BlockV2(...) and ensure error handling and variable names
(resp, respErr, retryableError, ret, err) remain consistent with the surrounding
backoff.Retry block.

In `@ops/docker-sequencer-test/run-test.sh`:
- Around line 62-76: The wait_for_block function currently loops forever; modify
wait_for_block to accept or derive a timeout (like wait_for_rpc) and track start
time, then inside the loop compute elapsed time and break with failure if
elapsed >= timeout; update log messages and the return code so it returns
non-zero on timeout and logs an error (use the same style as wait_for_rpc),
referencing the wait_for_block function, the target_height/local current
variable checks, and ensure sleep and echo behavior remain but are guarded by
the timeout check.

In `@ops/docker-sequencer-test/scripts/tx-generator.sh`:
- Line 9: The script defines PRIVATE_KEY but never uses it; either remove the
PRIVATE_KEY variable or change the transaction flow to sign and send a raw
transaction instead of calling eth_sendTransaction. If you choose to use it,
update the send step that currently calls eth_sendTransaction (around the
eth_sendTransaction usage) to create and sign a transaction with PRIVATE_KEY and
submit via eth_sendRawTransaction (i.e., build the tx, sign with the private
key, and send the resulting raw hex). If you choose to remove it, delete the
PRIVATE_KEY declaration and any related unused references so the script
consistently relies on the node's unlocked account.
- Around line 42-44: The script currently sets NONCE_HEX=$(get_nonce) and then
does arithmetic expansion NONCE=$((NONCE_HEX)) which will treat empty or non-hex
as 0; add validation after calling get_nonce to ensure NONCE_HEX is non-empty
and matches a hex regexp (e.g. ^[0-9a-fA-F]+$) before converting; if invalid,
either retry get_nonce or exit with an error to avoid nonce collisions; when
valid, convert explicitly (use base-16 conversion semantics) and then assign
NONCE and continue (references: get_nonce, NONCE_HEX, NONCE).

---

Outside diff comments:
In `@node/types/retryable_client.go`:
- Around line 445-461: BlockByNumber currently calls rc.ethClient directly,
bypassing the client-switching logic in rc.eClient(); change the call to use
rc.eClient().BlockByNumber so the method always uses the active client returned
by eClient(), preserve the existing retry/backoff flow and error handling
(including retryableError checks and setting err/ret), and ensure ret is only
assigned when resp is non-nil; update references inside the anonymous retry func
to use eClient() instead of ethClient.

---

Nitpick comments:
In `@contracts/contracts/l1/L1Sequencer.sol`:
- Around line 9-13: The contract L1Sequencer (which inherits OwnableUpgradeable)
lacks a reserved storage gap and so adding new state vars in upgrades could
corrupt the storage layout; add a private reserved storage array (e.g.,
uint256[n] __gap) after the declared state variable `address public sequencer;`
(inside the storage section of L1Sequencer) to reserve future slots and preserve
compatibility with OwnableUpgradeable and future upgrades.

In `@node/core/executor.go`:
- Around line 662-683: executableL2DataToBlockV2 currently copies Transactions
directly and can leave BlockV2.Transactions nil, making it inconsistent with
blockV2ToExecutableL2Data which ensures Transactions is never nil; update
executableL2DataToBlockV2 to defensive-copy or set Transactions to an empty
slice when data.Transactions is nil (i.e., ensure BlockV2.Transactions =
data.Transactions or make([]<txnType>, 0) if nil) so both conversion functions
guarantee a non-nil Transactions slice.

In `@node/flags/flags.go`:
- Around line 3-6: The import block in flags.go violates Go convention by
placing the third-party package github.com/urfave/cli before the standard
library package time; reorder the imports so standard library packages (time)
appear first, then a blank line, then third-party packages
(github.com/urfave/cli) to follow Go import grouping conventions.

In `@node/l1sequencer/signer.go`:
- Around line 65-71: The constructor/usage mismatch: NewLocalSigner currently
accepts a nil verifier but IsActiveSequencer treats nil verifier as an error;
pick one of two fixes and implement it consistently: (A) If verifier is
optional, change LocalSigner.IsActiveSequencer to return (false, nil) when
s.verifier == nil (remove the fmt.Errorf path) so caller sees "not active"
without error; or (B) If verifier is required, validate in NewLocalSigner (e.g.,
return an error) and ensure NewLocalSigner does not construct a LocalSigner with
a nil verifier. Refer to the LocalSigner type, the IsActiveSequencer method, and
NewLocalSigner to locate where to apply the change.

In `@node/l1sequencer/tracker.go`:
- Around line 54-68: The loop in L1Tracker.loop delays the first sync check by
up to one minute; call t.checkL1SyncLag() once immediately before entering the
for/select loop so the first check runs on startup (ensure you still respect
t.ctx.Done() by returning if context is canceled), then keep the existing
ticker-driven checks; reference the L1Tracker.loop function and the
checkL1SyncLag method to locate where to add the immediate invocation.

In `@node/l1sequencer/verifier.go`:
- Around line 59-83: IsSequencer can perform two RPC calls by calling flushCache
twice (once for expired cache and again on cache miss); modify IsSequencer to
avoid a redundant second refresh by tracking whether a refresh already occurred
in this invocation—call flushCache once if cache is expired, set a local flag
(e.g., refreshed), check the cached sequencer (c.sequencer) and only call
flushCache again on miss if refreshed is false; keep the existing mutex locking
and return semantics and reference functions/fields IsSequencer, flushCache,
c.cacheExpiry, c.sequencer, and c.mutex.

In `@node/sync/bridge_client.go`:
- Around line 27-30: Change BridgeClient.L1Client to return a narrow read-only
interface instead of *ethclient.Client: declare a new interface (e.g.,
ReadOnlyEthClient or L1ClientReader) in the same package that lists only the
methods callers actually use, update the method signature func (c *BridgeClient)
L1Client() ReadOnlyEthClient, and return c.l1Client (ethclient.Client implements
the interface). This preserves behavior but prevents callers from calling
mutating methods like Close(); ensure the interface includes all methods
currently invoked on the returned client so the code continues to compile.

In `@ops/docker-sequencer-test/docker-compose.override.yml`:
- Line 3: Remove the obsolete top-level "version:" key from
docker-compose.override.yml (the "version: '3.8'" line) because Compose v2+
ignores it and emits a warning; simply delete that key and its value so the file
uses the newer schema without the version field.
- Line 27: Add an inline comment next to the MORPH_NODE_SEQUENCER_PRIVATE_KEY
environment variable to clearly state that this value is a pre-generated
test/development private key (not for production) so scanners and future readers
know it's safe to commit; update the same comment for the other identical key
occurrence (the second MORPH_NODE_SEQUENCER_PRIVATE_KEY entry) to match.

In `@ops/docker-sequencer-test/Dockerfile.l2-geth-test`:
- Around line 12-17: The runtime stage currently uses the full builder image
(ghcr.io/morph-l2/go-ubuntu-builder:go-1.24-ubuntu); replace it with a minimal
runtime base such as ubuntu:22.04 or debian:bookworm-slim, install only the
runtime packages (ca-certificates, bash, curl) as already present, and adjust
any subsequent COPY/USER instructions that expect the built binary (ensure the
compiled binary built in the builder stage is copied into the new minimal stage
and ownership/permissions are set appropriately). Keep the existing RUN apt-get
lines but run them against the new minimal base and remove any leftover
build-tool assumptions from the runtime stage.

In `@ops/docker-sequencer-test/Dockerfile.l2-node-test`:
- Around line 37-47: The final stage currently uses the full builder image (FROM
ghcr.io/morph-l2/go-ubuntu-builder:go-1.24-ubuntu) even though only the compiled
binaries are needed; change the final stage to a minimal runtime base (e.g., a
distroless or slim image) and re-add any required runtime packages
(ca-certificates or libc) if needed, then keep the COPY lines (COPY
--from=builder /bitget/morph/node/build/bin/tendermint /usr/local/bin/ and COPY
--from=builder /bitget/morph/node/build/bin/morphnode /usr/local/bin/) and the
CMD (["morphnode","--home","/data"]) so only the two binaries are included in
the smaller image.

In `@ops/docker-sequencer-test/run-test.sh`:
- Line 471: The echo line has an extra leading space causing inconsistent
indentation; remove the extra leading space so the line reads starting at column
1 (i.e., align `echo "Node 0 (seq-0): Block $(get_block_number
http://127.0.0.1:8545)"` with surrounding lines), keeping the same content and
using the existing `get_block_number` call.
- Line 274: The exported SEQUENCER_PRIVATE_KEY value in run-test.sh is the
well-known Anvil/Hardhat account `#0` private key used for local/dev testing; add
an inline comment next to the export of SEQUENCER_PRIVATE_KEY clarifying that
this is the intentional, widely-used development/test-only key (not for
production) so readers understand it's safe and expected in tests.
- Line 68: Several local variables are declared with command substitution in one
step (e.g., local current=$(get_block_number "$rpc_url")), which triggers
ShellCheck SC2155; change each to declare the variable first and assign
separately to avoid masking return values. For each occurrence (functions like
get_block_number and variables such as current, target, next, etc.), replace the
single-line "local var=$(...)" with two lines: "local var" followed by
"var=$(...)" ensuring you preserve any quoting and command arguments (e.g.,
"$rpc_url"); apply this pattern to all listed occurrences.
- Around line 142-240: Extract the large inline Python payload into a separate
script (e.g., generate_genesis.py) and replace the embedded python3 -c "..."
block in run-test.sh with a call to that script; move all top-level symbols and
logic (sys.path.insert(0, '$DEVNET_DIR'), imports devnet, functions/variables
like run_command, read_json, write_json, log, devnet_cfg_orig,
temp_deploy_config, genesis_batch_header, addresses, etc.) into the new file,
accept required environment values (DEVNET_DIR, MORPH_ROOT, OPS_DIR,
CONTRACTS_DIR or pass them as args/env), add a proper shebang and make it
executable, and ensure the shell script invokes python3 generate_genesis.py with
the same environment so return codes and side-effects (files written to
devnet_dir, deployment_dir, .env updates) are preserved.

In `@ops/docker-sequencer-test/scripts/tx-generator.sh`:
- Around line 31-36: The get_block_number function is duplicated between
tx-generator.sh and run-test.sh; extract this function into a shared utility
script (e.g., a sourced common util script) and replace the duplicate
definitions by sourcing that shared script from both tx-generator.sh and
run-test.sh, ensuring the function name get_block_number remains unchanged so
callers keep working and update any shebangs/execute permissions as needed.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb760d0 and 77f2f74.

⛔ Files ignored due to path filters (1)
  • go.work is excluded by !**/*.work
📒 Files selected for processing (30)
  • bindings/bindings/l1sequencer.go
  • contracts/contracts/l1/L1Sequencer.sol
  • contracts/deploy/013-DeployProxys.ts
  • contracts/deploy/014-DeployImpls.ts
  • contracts/deploy/019-AdminTransfer.ts
  • contracts/deploy/022-SequencerInit.ts
  • contracts/deploy/index.ts
  • contracts/src/types.ts
  • contracts/tasks/deploy.ts
  • node/blocktag/config.go
  • node/blocktag/service.go
  • node/cmd/node/main.go
  • node/core/executor.go
  • node/flags/flags.go
  • node/go.mod
  • node/l1sequencer/signer.go
  • node/l1sequencer/tracker.go
  • node/l1sequencer/verifier.go
  • node/sequencer/tm_node.go
  • node/sync/bridge_client.go
  • node/sync/syncer.go
  • node/types/retryable_client.go
  • ops/devnet-morph/devnet/__init__.py
  • ops/docker-sequencer-test/Dockerfile.l2-geth-test
  • ops/docker-sequencer-test/Dockerfile.l2-node-test
  • ops/docker-sequencer-test/README.md
  • ops/docker-sequencer-test/docker-compose.override.yml
  • ops/docker-sequencer-test/entrypoint-l2.sh
  • ops/docker-sequencer-test/run-test.sh
  • ops/docker-sequencer-test/scripts/tx-generator.sh
💤 Files with no reviewable changes (1)
  • node/blocktag/config.go
✅ Files skipped from review due to trivial changes (1)
  • ops/docker-sequencer-test/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • node/go.mod

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@contracts/go.mod`:
- Line 9: Update the contracts module to account for go-ethereum commit
a805e930447c breaking changes: change any receipt encoding/decoding or storage
logic that expects Receipt.Reference as *common.Reference to handle []byte and
treat Version, Reference, Memo as optional (nil/empty) when
serializing/deserializing; update reorg handlers (any functions named like
handleReorg, processReorg, or index cleanup routines) to assume reference index
entries may be deleted for reorged transactions and avoid relying on stale
indexes; modify code using MorphTx.copy() (and any callers that access
MorphTx.FeeLimit) to handle FeeLimit possibly being nil after copy (add nil
checks or initialize FeeLimit where needed); update imports/usages of EVM trace
loggers and EIP-712 types to their new packages and rename opcode constants
(replace SHA3→KECCAK256, SUICIDE→SELFDESTRUCT) wherever referenced. Ensure all
changes touch the symbols Receipt, Reference, MorphTx.copy / MorphTx.FeeLimit,
and reorg/index handler functions.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9166af4 and 8113f32.

⛔ Files ignored due to path filters (9)
  • bindings/go.sum is excluded by !**/*.sum
  • contracts/go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
  • node/go.sum is excluded by !**/*.sum
  • ops/l2-genesis/go.sum is excluded by !**/*.sum
  • ops/tools/go.sum is excluded by !**/*.sum
  • oracle/go.sum is excluded by !**/*.sum
  • token-price-oracle/go.sum is excluded by !**/*.sum
  • tx-submitter/go.sum is excluded by !**/*.sum
📒 Files selected for processing (10)
  • Makefile
  • bindings/go.mod
  • contracts/go.mod
  • go-ethereum
  • node/go.mod
  • ops/l2-genesis/go.mod
  • ops/tools/go.mod
  • oracle/go.mod
  • token-price-oracle/go.mod
  • tx-submitter/go.mod
✅ Files skipped from review due to trivial changes (1)
  • go-ethereum
🚧 Files skipped from review as they are similar to previous changes (6)
  • ops/l2-genesis/go.mod
  • Makefile
  • oracle/go.mod
  • token-price-oracle/go.mod
  • ops/tools/go.mod
  • bindings/go.mod

contracts/go.mod Outdated
require (
github.com/iden3/go-iden3-crypto v0.0.16
github.com/morph-l2/go-ethereum v1.10.14-0.20260211074551-4f0f6e6bd141
github.com/morph-l2/go-ethereum v1.10.14-0.20260227035937-a805e930447c
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the go-ethereum commit exists and check for breaking changes

echo "Checking if commit a805e930447c exists in morph-l2/go-ethereum..."
gh api repos/morph-l2/go-ethereum/commits/a805e930447c --jq '.sha, .commit.message' 2>&1 || echo "Commit check failed or not found"

echo -e "\n--- Checking commit details ---"
gh api repos/morph-l2/go-ethereum/commits/a805e930447c --jq '.commit.author.date, .commit.message' 2>&1 || echo "Unable to retrieve commit details"

Repository: morph-l2/morph

Length of output: 509


🌐 Web query:

morph-l2/go-ethereum commit a805e930447c breaking changes

💡 Result:

Commit a805e930447c (“fix sth”) in morph-l2/go-ethereum introduces these potentially breaking changes (mainly for downstream code and persisted-data assumptions):

  • Receipt RLP schema change (Morph-specific fields): In storedReceiptRLP (used for receipt storage/encoding), Version, Reference, and Memo were changed to rlp:"optional", and Reference’s type changed from *common.Reference to []byte to avoid decode issues when empty. Any code depending on the old field types / always-present fields (or decoding receipts with the old exact struct layout) may break. [1]
  • Reorg behavior change (index cleanup): During chain reorgs, the code now additionally deletes reference index entries for “truly deleted” MorphTx transactions by iterating the old chain to get block context (timestamp, tx index). If tooling relied on the previous (leakier) index behavior, results can change. [1]
  • MorphTx.copy() semantics for FeeLimit: The copy logic no longer pre-allocates FeeLimit; instead it allocates it only when non-nil (cpy.FeeLimit = new(big.Int).Set(tx.FeeLimit)). Code that assumed FeeLimit is always non-nil after copying may break. [1]

Sources: [1]


🌐 Web query:

go-ethereum v1.10.14 breaking changes migration

💡 Result:

go-ethereum (geth) v1.10.14: breaking changes / migration notes

  1. “Fast sync” is removed
  • Impact: nodes/configs using --syncmode fast will fail to start or must be changed.
  • Migration: switch to snap sync (default in the 1.10.x line) or full sync (e.g., --syncmode=snap or --syncmode=full). [2]
  • This removal is also called out by downstream operators as a startup-breaking config change. [3]
  1. Go library / internal API moves that can break builds
    If you embed geth as a library or compile tools against its packages, v1.10.14 includes package/layout changes:
  • Built-in EVM trace loggers moved from core/vm to a dedicated package (update imports/usages accordingly). [1]
  • EIP-712 typed-data structs moved from signer/core to signer/core/apitypes (update imports). [1]
  1. Opcode name changes (source-compat implications for tooling)
  • Internal opcode names were modernized (e.g., SHA3 → KECCAK256, SUICIDE → SELFDESTRUCT). If your tooling matches on these names (debuggers/tracers/log parsers), update expectations. [1]

Primary reference: the v1.10.14 release notes (“Hourglass Nebula”). [1]


Update contracts module for go-ethereum breaking changes in commit a805e930447c.

This Morph-specific commit introduces several breaking changes that will require code updates:

  • Receipt RLP schema: Reference field type changed from *common.Reference to []byte, and Version, Reference, Memo are now optional. Update any receipt encoding/decoding or storage logic.
  • Chain reorg behavior: Reference index cleanup now deletes index entries for reorg'd transactions. Update reorg handlers if they depend on index state.
  • MorphTx.copy() semantics: FeeLimit is no longer pre-allocated during copy. Update code that assumes FeeLimit is always non-nil after copying.
  • go-ethereum v1.10.14 changes: Fast sync is removed (migrate to snap sync), EVM trace loggers and EIP-712 types have moved packages (update imports), opcode names changed (SHA3 → KECCAK256, SUICIDE → SELFDESTRUCT).

Review and update the contracts module to handle these changes before merging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/go.mod` at line 9, Update the contracts module to account for
go-ethereum commit a805e930447c breaking changes: change any receipt
encoding/decoding or storage logic that expects Receipt.Reference as
*common.Reference to handle []byte and treat Version, Reference, Memo as
optional (nil/empty) when serializing/deserializing; update reorg handlers (any
functions named like handleReorg, processReorg, or index cleanup routines) to
assume reference index entries may be deleted for reorged transactions and avoid
relying on stale indexes; modify code using MorphTx.copy() (and any callers that
access MorphTx.FeeLimit) to handle FeeLimit possibly being nil after copy (add
nil checks or initialize FeeLimit where needed); update imports/usages of EVM
trace loggers and EIP-712 types to their new packages and rename opcode
constants (replace SHA3→KECCAK256, SUICIDE→SELFDESTRUCT) wherever referenced.
Ensure all changes touch the symbols Receipt, Reference, MorphTx.copy /
MorphTx.FeeLimit, and reorg/index handler functions.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Makefile`:
- Around line 2-3: The Makefile pins the submodule to the moving main branch via
ETHEREUM_SUBMODULE_COMMIT_OR_TAG while ETHEREUM_TARGET_VERSION is a
pseudo-version tied to commit 81fb39343fd1106e6e87ea6aa91a458f7e458e08; update
ETHEREUM_SUBMODULE_COMMIT_OR_TAG to that exact commit hash (the one encoded by
ETHEREUM_TARGET_VERSION / shown in .gitmodules) so the submodule checkout
matches the go-ethereum revision used by go.mod and restores reproducible builds
for targets like submodules, devnet-up, and geth.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc2e1822-2555-4830-a6bc-f51cae2b9ac1

📥 Commits

Reviewing files that changed from the base of the PR and between 8113f32 and 3a0acf7.

⛔ Files ignored due to path filters (9)
  • bindings/go.sum is excluded by !**/*.sum
  • contracts/go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
  • node/go.sum is excluded by !**/*.sum
  • ops/l2-genesis/go.sum is excluded by !**/*.sum
  • ops/tools/go.sum is excluded by !**/*.sum
  • oracle/go.sum is excluded by !**/*.sum
  • token-price-oracle/go.sum is excluded by !**/*.sum
  • tx-submitter/go.sum is excluded by !**/*.sum
📒 Files selected for processing (10)
  • Makefile
  • bindings/go.mod
  • contracts/go.mod
  • go-ethereum
  • node/go.mod
  • ops/l2-genesis/go.mod
  • ops/tools/go.mod
  • oracle/go.mod
  • token-price-oracle/go.mod
  • tx-submitter/go.mod
🚧 Files skipped from review as they are similar to previous changes (7)
  • contracts/go.mod
  • node/go.mod
  • token-price-oracle/go.mod
  • go-ethereum
  • tx-submitter/go.mod
  • bindings/go.mod
  • oracle/go.mod

Comment on lines +2 to +3
ETHEREUM_SUBMODULE_COMMIT_OR_TAG := main
ETHEREUM_TARGET_VERSION := v1.10.14-0.20260306065034-81fb39343fd1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Makefile Ethereum pins =="
grep -nE '^(ETHEREUM_SUBMODULE_COMMIT_OR_TAG|ETHEREUM_TARGET_VERSION) :=' Makefile

echo
echo "== Superproject-pinned go-ethereum submodule revision =="
git submodule status -- go-ethereum || true

echo
echo "== Commit suffix encoded in ETHEREUM_TARGET_VERSION =="
python - <<'PY'
import re
from pathlib import Path

text = Path("Makefile").read_text()
m = re.search(r'ETHEREUM_TARGET_VERSION := v[0-9.]+-\d+-([0-9a-f]{12})', text)
print(m.group(1) if m else "not found")
PY

echo
echo "== Current go-ethereum HEAD after checkout on this branch =="
git -C go-ethereum rev-parse HEAD || true

echo
echo "Expected: the submodule checkout target should be a fixed revision matching the pseudo-version commit, not a moving branch name."

Repository: morph-l2/morph

Length of output: 608


Pin the submodule to the same go-ethereum revision as the module graph.

Line 2 points the submodule at the moving main branch, while Line 3 pins Go modules to a specific pseudo-version ending in 81fb39343fd1. The superproject's .gitmodules pins the submodule to commit 81fb39343fd1106e6e87ea6aa91a458f7e458e08, but the Makefile's submodules target later forces a git checkout main, which moves HEAD away from that pin. This causes devnet-up and geth to build against a different go-ethereum tree than your go.mod files were updated to. Pin Line 2 to the commit hash encoded in Line 3 to restore reproducibility.

Proposed fix
-ETHEREUM_SUBMODULE_COMMIT_OR_TAG := main
+ETHEREUM_SUBMODULE_COMMIT_OR_TAG := 81fb39343fd1106e6e87ea6aa91a458f7e458e08
 ETHEREUM_TARGET_VERSION := v1.10.14-0.20260306065034-81fb39343fd1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ETHEREUM_SUBMODULE_COMMIT_OR_TAG := main
ETHEREUM_TARGET_VERSION := v1.10.14-0.20260306065034-81fb39343fd1
ETHEREUM_SUBMODULE_COMMIT_OR_TAG := 81fb39343fd1106e6e87ea6aa91a458f7e458e08
ETHEREUM_TARGET_VERSION := v1.10.14-0.20260306065034-81fb39343fd1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` around lines 2 - 3, The Makefile pins the submodule to the moving
main branch via ETHEREUM_SUBMODULE_COMMIT_OR_TAG while ETHEREUM_TARGET_VERSION
is a pseudo-version tied to commit 81fb39343fd1106e6e87ea6aa91a458f7e458e08;
update ETHEREUM_SUBMODULE_COMMIT_OR_TAG to that exact commit hash (the one
encoded by ETHEREUM_TARGET_VERSION / shown in .gitmodules) so the submodule
checkout matches the go-ethereum revision used by go.mod and restores
reproducible builds for targets like submodules, devnet-up, and geth.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants